package models

import (
	"encoding/json"
	"fmt"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"
)

var DB *gorm.DB

func findRootDir() (string, error) {
	// 假设go.mod文件位于你的项目根目录中
	root, err := os.Getwd()
	if err != nil {
		return "", err
	}

	// 向上循环查找直到找到go.mod文件
	for {
		if _, err := os.Stat(filepath.Join(root, "go.mod")); os.IsNotExist(err) {
			root = filepath.Dir(root)
			if root == "/" {
				// 到达文件系统的根目录还没找到go.mod文件
				return "", err
			}
		} else {
			break
		}
	}
	return root, nil
}

func InitModel() {
	var config map[string]string

	root, err := findRootDir()
	if err != nil {
		log.Fatal("Cannot find root directory", err)
	}

	configFile := filepath.Join(root, "db_config.json")
	file, err := os.Open(configFile)

	if err != nil {
		log.Fatal("Cannot open db_config.json file", err)
	}

	defer file.Close()

	if err := json.NewDecoder(file).Decode(&config); err != nil {
		log.Fatal("Cannot get db configuration from file", err)
	}

	dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=%s TimeZone=%s",
		config["host"], config["user"], config["password"], config["dbname"], config["port"], config["sslmode"], config["timezone"])

	gormLogger := logger.Default.LogMode(logger.Info)

	maxIdleConns := 10
	maxOpenConns := 100
	retryInterval := time.Second * 3

	for {
		DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
			Logger: gormLogger,
		})

		if err != nil {
			fmt.Println("Cannot connect to database postgres, retrying in", retryInterval)
			time.Sleep(retryInterval)
			continue
		} else {
			fmt.Println("We are connected to the database postgres")
		}

		sqlDB, err := DB.DB()
		if err != nil {
			fmt.Println("Cannot get sql.DB from gorm.DB")
			log.Fatal("connection error:", err)
			return
		}

		sqlDB.SetMaxIdleConns(maxIdleConns)
		sqlDB.SetMaxOpenConns(maxOpenConns)

		err = DB.AutoMigrate(&User{}, &Request{}, &Response{}, &TransactionDetail{}, &BrokerIncomeSummary{})
		if err != nil {
			fmt.Println("Cannot migrate table, retrying in", retryInterval)
			time.Sleep(retryInterval)
			continue
		} else {
			fmt.Println("Tables migrated successfully")
		}

		addDBHooks(DB)

		break
	}
}

func addDBHooks(db *gorm.DB) {
	// 添加钩子，用于在每次操作后检查错误
	db.Callback().Create().After("gorm:create").Register("my_plugin:reconnectOnFailure", reconnectOnFailure)
	db.Callback().Update().After("gorm:update").Register("my_plugin:reconnectOnFailure", reconnectOnFailure)
	db.Callback().Delete().After("gorm:delete").Register("my_plugin:reconnectOnFailure", reconnectOnFailure)
	db.Callback().Query().After("gorm:query").Register("my_plugin:reconnectOnFailure", reconnectOnFailure)
	// 可以为其他操作类型添加更多钩子
}

var initMutex sync.Mutex

func reconnectOnFailure(db *gorm.DB) {
	if db.Error != nil {
		sqlDB, err := db.DB()
		if err != nil {
			// 如果无法获取底层的 sql.DB 对象，可能需要记录错误或者采取其他措施
			return
		}

		// 尝试 ping 数据库
		if err = sqlDB.Ping(); err != nil {
			fmt.Println("Lost database connection, attempting to reconnect...")

			// 加锁
			initMutex.Lock()
			defer initMutex.Unlock()

			// 再次检查数据库连接状态
			if err = sqlDB.Ping(); err != nil {
				// 如果数据库依然未连接，则重新初始化
				InitModel()
			} else {
				// 如果数据库已经重新连接，则无需再次初始化
				fmt.Println("Database connection has been re-established by another process.")
			}
		}
	}
}
